package player;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import songstreams.SongStream;
import com.xuggle.ferry.JNIMemoryManager;
import com.xuggle.ferry.JNIMemoryManager.MemoryModel;
import com.xuggle.xuggler.IAudioSamples;
import com.xuggle.xuggler.ICodec;
import com.xuggle.xuggler.IContainer;
import com.xuggle.xuggler.IPacket;
import com.xuggle.xuggler.IStream;
import com.xuggle.xuggler.IStreamCoder;
public class XugglePlayer implements Runnable {
private static SourceDataLine mLine;
private boolean shouldStop;
private boolean shouldPause;
private PlayerListener listener;
private SongStream stream;
public XugglePlayer(SongStream stream, PlayerListener listener) {
this.stream = stream;
this.listener = listener;
}
@Override
public void run() {
JNIMemoryManager.setMemoryModel(MemoryModel.NATIVE_BUFFERS);
try {
if (!stream.open(IContainer.Type.READ, null))
throw new RuntimeException("Error opening stream: "
+ stream.toString());
IContainer container = stream.getContainer();
int streamCount = container.getNumStreams();
int audioStreamId = -1;
IStreamCoder coder = null;
for (int i = 0; i < streamCount; i++) {
IStream stream = container.getStream(i);
coder = stream.getStreamCoder();
if (coder.getCodecType().equals(ICodec.Type.CODEC_TYPE_AUDIO)) {
audioStreamId = i;
break;
}
}
if (audioStreamId == -1)
throw new RuntimeException(
"Could not find audio stream in stream: "
+ stream.toString());
if (coder.open(null, null) < 0)
throw new RuntimeException(
"Could not open audio decoder for stream: "
+ stream.toString());
openJavaSound(coder);
IPacket packet = IPacket.make();
listener.startedPlayback(stream);
while (container.readNextPacket(packet) >= 0 && !shouldStop) {
if (packet.getStreamIndex() == audioStreamId) {
IAudioSamples samples = IAudioSamples.make(1024,
coder.getChannels());
int offset = 0;
while (offset < packet.getSize()) {
int bytesDecoded = coder.decodeAudio(samples, packet,
offset);
if (bytesDecoded < 0)
throw new RuntimeException(
"Error decoding audio from stream: "
+ stream.toString());
offset += bytesDecoded;
if (samples.isComplete())
playJavaSound(samples);
}
}
}
closeJavaSound();
if (coder != null) {
coder.close();
coder = null;
}
if (container != null) {
container.close();
coder = null;
}
} catch (IOException | InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void playJavaSound(IAudioSamples samples) throws InterruptedException {
while(shouldPause)
Thread.sleep(50);
byte[] bytes = samples.getData().getByteArray(0, samples.getSize());
mLine.write(bytes, 0, bytes.length);
}
private void openJavaSound(IStreamCoder coder) {
AudioFormat af = new AudioFormat(
coder.getSampleRate(),
(int) IAudioSamples.findSampleBitDepth(coder.getSampleFormat()),
coder.getChannels(), true, false);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, af);
try {
mLine = (SourceDataLine) AudioSystem.getLine(info);
mLine.open(af);
mLine.start();
} catch (LineUnavailableException e) {
throw new RuntimeException("Could not open data line");
}
}
private void closeJavaSound() {
if(!shouldStop)
listener.stoppedPlayback(stream);
if (mLine != null) {
mLine.drain();
/*
* Close the line.
*/
mLine.close();
mLine = null;
}
}
public void pause() {
this.shouldPause = true;
}
public void stop() {
this.shouldStop = true;
}
public void resume() {
this.shouldPause = false;
}
}